package mysql

import (
	"fmt"
	"strings"

	"GameServer/utils"
)

// sql builder.
type SQLBuilder struct {
	sql string
	has bool
}

// new builder.
func NewSQLBuilder() *SQLBuilder {
	builder := &SQLBuilder{}
	builder.Reset()
	return builder
}

func (sb *SQLBuilder) Reset() {
	sb.has = false
	sb.sql = ""
}

func (sb *SQLBuilder) HasCondition() bool {
	return sb.has
}

// build not in for string.
func (sb *SQLBuilder) BuildConditionNotIn(field string, value string) *SQLBuilder {
	BuildConditionNotIn(&sb.sql, &sb.has, field, value)
	return sb
}

// build in for string.
func (sb *SQLBuilder) BuildConditionIn(field string, value string) *SQLBuilder {
	BuildConditionIn(&sb.sql, &sb.has, field, value)
	return sb
}

// build big.
func (sb *SQLBuilder) BuildConditionBig(field string, value string) *SQLBuilder {
	BuildConditionBig(&sb.sql, &sb.has, field, value)
	return sb
}

// build less.
func (sb *SQLBuilder) BuildConditionLess(field string, value string) *SQLBuilder {
	BuildConditionLess(&sb.sql, &sb.has, field, value)
	return sb
}

// build int = for string
func (sb *SQLBuilder) BuildCondition(field string, value string, referenced bool) (*SQLBuilder, bool) {
	return sb, BuildCondition(&sb.sql, &sb.has, field, value, referenced)
}

// build int = for int
func (sb *SQLBuilder) BuildConditionInt(field string, value int64, referenced bool) (*SQLBuilder, bool) {
	return sb, BuildConditionInt(&sb.sql, &sb.has, field, value, referenced)
}

// build int <>
func (sb *SQLBuilder) BuildConditionNot(field string, value string) *SQLBuilder {
	BuildConditionNot(&sb.sql, &sb.has, field, value)
	return sb
}

// build string =
func (sb *SQLBuilder) BuildConditionStr(field string, value string) *SQLBuilder {
	BuildConditionStr(&sb.sql, &sb.has, field, value)
	return sb
}

// build like
func (sb *SQLBuilder) BuildConditionLike(field string, value string, referenced bool) *SQLBuilder {
	BuildConditionLike(&sb.sql, &sb.has, field, value, referenced)
	return sb
}

// get string
func (sb *SQLBuilder) String() string {
	return sb.sql
}

// 获取所有查询条数
func GetAllConditionCount(client MysqlExecutorProxy, table string,
	hasCondition bool, condition string) (uint32, error) {
	type selectNum struct {
		Num uint32 `sql:"num"`
	}
	var arNum []selectNum
	selectNumSQL := "SELECT COUNT(*) num FROM " + table
	if hasCondition {
		selectNumSQL += " " + condition + ";"
	} else {
		selectNumSQL += ";"
	}

	if er := QueryToStruct(client, &arNum, selectNumSQL); er != nil {
		return 0, er
	} else {
		return arNum[0].Num, nil
	}
}

// update a table with field and its value.
// 如果主key是字符串，必须自己去Escape.
func UpdateTableFields(client MysqlExecutorProxy, tableName string,
	priKey string, priKeyValue string,
	fields []string, values []interface{},
) (int64, error) {
	if len(fields) != len(values) {
		return 0, fmt.Errorf("更新的field和value参数个数不相等")
	}

	// build sql statement.
	updateSQL := "UPDATE " + tableName + " SET "
	for i := 0; i < len(fields); i++ {
		updateSQL += "`" + fields[i] + "` = ?"
		if i != len(fields)-1 {
			updateSQL += ","
		}
	}
	updateSQL += " WHERE `" + priKey + "` =" + priKeyValue + ";"

	res, err := client.Exec(updateSQL, values...)
	if err != nil {
		return 0, err
	} else {
		rowAffect, _ := res.RowsAffected()
		return rowAffect, nil
	}
}

// build not in.
func BuildConditionNotIn(sql *string, beforCondition *bool, field string, value string) {
	if value != "" {
		valueList := strings.Split(value, ",")

		var valueGroup string
		for i := 0; i < len(valueList); i++ {
			valueGroup += "'" + string(EscapeString(valueList[i])) + "'"
			if i != len(valueList)-1 {
				valueGroup += ","
			}
		}

		if *beforCondition {
			*sql += " AND `" + field + "` NOT IN(" + valueGroup + ")"
		} else {
			*sql += " WHERE `" + field + "` NOT IN(" + valueGroup + ")"
		}
		*beforCondition = true
	}
}

// build in for string
func BuildConditionIn(sql *string, beforCondition *bool, field string, value string) {
	if value != "" {
		valueList := strings.Split(value, ",")
		var valueGroup string
		for i := 0; i < len(valueList); i++ {
			valueGroup += "'" + string(EscapeString(valueList[i])) + "'"
			if i != len(valueList)-1 {
				valueGroup += ","
			}
		}

		if *beforCondition {
			*sql += " AND `" + field + "` IN(" + valueGroup + ")"
		} else {
			*sql += " WHERE `" + field + "` IN(" + valueGroup + ")"
		}
		*beforCondition = true
	}
}

// build big for string.
func BuildConditionBig(sql *string, beforCondition *bool, field string, value string) {
	if value != "" {
		escValue := string(EscapeString(value))
		if *beforCondition {
			*sql += " AND `" + field + "` >='" + escValue + "'"
		} else {
			*sql += " WHERE `" + field + "` >='" + escValue + "'"
		}
		*beforCondition = true
	}
}

// build less for string.
func BuildConditionLess(sql *string, beforCondition *bool, field string, value string) {
	if value != "" {
		escValue := string(EscapeString(value))
		if *beforCondition {
			*sql += " AND `" + field + "` <='" + escValue + "'"
		} else {
			*sql += " WHERE `" + field + "` <='" + escValue + "'"
		}
		*beforCondition = true
	}
}

// build int = value形式，无须escape,对应的value如果不是number,就返回
// 其中referenced表示是否需要"去引用字段，下同。
// 数字型的如果不是数字，就返回false,下同。
func BuildCondition(sql *string, beforCondition *bool, field string,
	value string, referenced bool,
) bool {
	if value != "" {
		if !utils.IsNumber(value) {
			return false
		}
		if *beforCondition {
			if referenced {
				*sql += " AND `" + field + "` =" + value
			} else {
				*sql += " AND " + field + " =" + value
			}
		} else {
			if referenced {
				*sql += " WHERE `" + field + "` =" + value
			} else {
				*sql += " WHERE " + field + " =" + value
			}
		}
		*beforCondition = true
	}
	return true
}

// build int
func BuildConditionInt(sql *string, beforCondition *bool, field string,
	value int64, referenced bool,
) bool {
	if *beforCondition {
		if referenced {
			*sql += " AND `" + field + "` =" + utils.Itoa(int(value))
		} else {
			*sql += " AND " + field + " =" + utils.Itoa(int(value))
		}
	} else {
		if referenced {
			*sql += " WHERE `" + field + "` =" + utils.Itoa(int(value))
		} else {
			*sql += " WHERE " + field + " =" + utils.Itoa(int(value))
		}
	}
	*beforCondition = true
	return true
}

// build int <>.
func BuildConditionNot(sql *string, beforContition *bool, field string, value string) bool {
	if value != "" {
		if !utils.IsNumber(value) {
			return false
		}

		if *beforContition {
			*sql += " AND `" + field + "` <> " + value
		} else {
			*sql += " WHERE `" + field + "` <> " + value
		}
		*beforContition = true
	}
	return true
}

// build str =
func BuildConditionStr(sql *string, beforCondition *bool, field string, value string) {
	if value != "" {
		escValue := string(EscapeString(value))
		if *beforCondition {
			*sql += " AND `" + field + "` ='" + escValue + "'"
		} else {
			*sql += " WHERE `" + field + "` ='" + escValue + "'"
		}
		*beforCondition = true
	}
}

// build like
func BuildConditionLike(sql *string, beforCondition *bool, field string, value string, referenced bool) {
	if value != "" {
		escValue := string(EscapeString(value))
		if *beforCondition {
			if referenced {
				*sql += " AND `" + field + "` LIKE '%" + escValue + "%'"
			} else {
				*sql += " AND " + field + " LIKE '%" + escValue + "%'"
			}
		} else {
			if referenced {
				*sql += " WHERE `" + field + "` LIKE '%" + escValue + "%'"
			} else {
				*sql += " WHERE " + field + " LIKE '%" + escValue + "%'"
			}
		}
		*beforCondition = true
	}
}
